/**
 * jquery.calendario.js v1.0.0
 * http://www.codrops.com
 *
 * Licensed under the MIT license.
 * http://www.opensource.org/licenses/mit-license.php
 *
 * Copyright 2012, Codrops
 * http://www.codrops.com
 */
;
(function ($, window, undefined) {

    'use strict';

    $.Calendario = function (options, element) {

        this.$el = $(element);
        this._init(options);

    };

    // the options
    $.Calendario.defaults = {
        /*
         you can also pass:
         month : initialize calendar with this month (1-12). Default is today.
         year : initialize calendar with this year. Default is today.
         caldata : initial data/content for the calendar.
         caldata format:
         {
         'MM-DD-YYYY' : 'HTML Content',
         'MM-DD-YYYY' : 'HTML Content',
         'MM-DD-YYYY' : 'HTML Content'
         ...
         }
         */
        weeks: [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ],
        weekabbrs: [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ],
        months: [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ],
        monthabbrs: [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ],
        // choose between values in options.weeks or options.weekabbrs
        displayWeekAbbr: false,
        // choose between values in options.months or options.monthabbrs
        displayMonthAbbr: false,
        // left most day in the calendar
        // 0 - Sunday, 1 - Monday, ... , 6 - Saturday
        startIn: 1,
        onDayClick: function ($el, $content, dateProperties) {
            return false;
        }
    };

    $.Calendario.prototype = {

        _init: function (options) {

            // options
            this.options = $.extend(true, {}, $.Calendario.defaults, options);

            this.today = new Date();
            this.month = ( isNaN(this.options.month) || this.options.month == null) ? this.today.getMonth() : this.options.month - 1;
            this.year = ( isNaN(this.options.year) || this.options.year == null) ? this.today.getFullYear() : this.options.year;
            this.caldata = this.options.caldata || {};
            this._generateTemplate();
            this._initEvents();

        },
        _initEvents: function () {

            var self = this;

            this.$el.on('click.calendario', 'div.fc-row > div', function () {

                var $cell = $(this),
                    idx = $cell.index(),
                    $content = $cell.children('div'),
                    dateProp = {
                        day: $cell.children('span.fc-date').text(),
                        month: self.month + 1,
                        monthname: self.options.displayMonthAbbr ? self.options.monthabbrs[ self.month ] : self.options.months[ self.month ],
                        year: self.year,
                        weekday: idx + self.options.startIn,
                        weekdayname: self.options.weeks[ idx + self.options.startIn ]
                    };

                if (dateProp.day) {
                    self.options.onDayClick($cell, $content, dateProp);
                }

            });

        },
        // Calendar logic based on http://jszen.blogspot.pt/2007/03/how-to-build-simple-calendar-with.html
        _generateTemplate: function (callback) {

            var head = this._getHead(),
                body = this._getBody(),
                rowClass;

            switch (this.rowTotal) {
                case 4 :
                    rowClass = 'fc-four-rows';
                    break;
                case 5 :
                    rowClass = 'fc-five-rows';
                    break;
                case 6 :
                    rowClass = 'fc-six-rows';
                    break;
            }

            this.$cal = $('<div class="fc-calendar ' + rowClass + '">').append(head, body);

            this.$el.find('div.fc-calendar').remove().end().append(this.$cal);

            if (callback) {
                callback.call();
            }

        },
        _getHead: function () {

            var html = '<div class="fc-head">';

            for (var i = 0; i <= 6; i++) {

                var pos = i + this.options.startIn,
                    j = pos > 6 ? pos - 6 - 1 : pos;

                html += '<div>';
                html += this.options.displayWeekAbbr ? this.options.weekabbrs[ j ] : this.options.weeks[ j ];
                html += '</div>';

            }

            html += '</div>';

            return html;

        },
        _getBody: function () {

            var d = new Date(this.year, this.month + 1, 0),
            // number of days in the month
                monthLength = d.getDate(),
                firstDay = new Date(this.year, this.month, 1);

            // day of the week
            this.startingDay = firstDay.getDay();

            var html = '<div class="fc-body"><div class="fc-row">',
            // fill in the days
                day = 1;

            // this loop is for weeks (rows)
            for (var i = 0; i < 7; i++) {

                // this loop is for weekdays (cells)
                for (var j = 0; j <= 6; j++) {

                    var pos = this.startingDay - this.options.startIn,
                        p = pos < 0 ? 6 + pos + 1 : pos,
                        inner = '',
                        today = this.month === this.today.getMonth() && this.year === this.today.getFullYear() && day === this.today.getDate(),
                        content = '';

                    if (day <= monthLength && ( i > 0 || j >= p )) {

                        inner += '<span class="fc-date">' + day + '</span><span class="fc-weekday">' + this.options.weekabbrs[ j + this.options.startIn > 6 ? j + this.options.startIn - 6 - 1 : j + this.options.startIn ] + '</span>';

                        // this day is:
                        var strdate = ( this.month + 1 < 10 ? '0' + ( this.month + 1 ) : this.month + 1 ) + '-' + ( day < 10 ? '0' + day : day ) + '-' + this.year,
                            dayData = this.caldata[ strdate ];

                        if (dayData) {
                            content = dayData;
                        }

                        if (content !== '') {
                            inner += '<div>' + content + '</div>';
                        }

                        ++day;

                    }
                    else {
                        today = false;
                    }

                    var cellClasses = today ? 'fc-today ' : '';
                    if (content !== '') {
                        cellClasses += 'fc-content';
                    }

                    html += cellClasses !== '' ? '<div class="' + cellClasses + '">' : '<div>';
                    html += inner;
                    html += '</div>';

                }

                // stop making rows if we've run out of days
                if (day > monthLength) {
                    this.rowTotal = i + 1;
                    break;
                }
                else {
                    html += '</div><div class="fc-row">';
                }

            }
            html += '</div></div>';

            return html;

        },
        // based on http://stackoverflow.com/a/8390325/989439
        _isValidDate: function (date) {

            date = date.replace(/-/gi, '');
            var month = parseInt(date.substring(0, 2), 10),
                day = parseInt(date.substring(2, 4), 10),
                year = parseInt(date.substring(4, 8), 10);

            if (( month < 1 ) || ( month > 12 )) {
                return false;
            }
            else if (( day < 1 ) || ( day > 31 )) {
                return false;
            }
            else if (( ( month == 4 ) || ( month == 6 ) || ( month == 9 ) || ( month == 11 ) ) && ( day > 30 )) {
                return false;
            }
            else if (( month == 2 ) && ( ( ( year % 400 ) == 0) || ( ( year % 4 ) == 0 ) ) && ( ( year % 100 ) != 0 ) && ( day > 29 )) {
                return false;
            }
            else if (( month == 2 ) && ( ( year % 100 ) == 0 ) && ( day > 29 )) {
                return false;
            }

            return {
                day: day,
                month: month,
                year: year
            };

        },
        _move: function (period, dir, callback) {

            if (dir === 'previous') {

                if (period === 'month') {
                    this.year = this.month > 0 ? this.year : --this.year;
                    this.month = this.month > 0 ? --this.month : 11;
                }
                else if (period === 'year') {
                    this.year = --this.year;
                }

            }
            else if (dir === 'next') {

                if (period === 'month') {
                    this.year = this.month < 11 ? this.year : ++this.year;
                    this.month = this.month < 11 ? ++this.month : 0;
                }
                else if (period === 'year') {
                    this.year = ++this.year;
                }

            }

            this._generateTemplate(callback);

        },
        /*************************
         ******PUBLIC METHODS *****
         **************************/
        getYear: function () {
            return this.year;
        },
        getMonth: function () {
            return this.month + 1;
        },
        getMonthName: function () {
            return this.options.displayMonthAbbr ? this.options.monthabbrs[ this.month ] : this.options.months[ this.month ];
        },
        // gets the cell's content div associated to a day of the current displayed month
        // day : 1 - [28||29||30||31]
        getCell: function (day) {

            var row = Math.floor(( day + this.startingDay - this.options.startIn ) / 7),
                pos = day + this.startingDay - this.options.startIn - ( row * 7 ) - 1;

            return this.$cal.find('div.fc-body').children('div.fc-row').eq(row).children('div').eq(pos).children('div');

        },
        setData: function (caldata) {

            caldata = caldata || {};
            $.extend(this.caldata, caldata);
            this._generateTemplate();

        },
        // goes to today's month/year
        gotoNow: function (callback) {

            this.month = this.today.getMonth();
            this.year = this.today.getFullYear();
            this._generateTemplate(callback);

        },
        // goes to month/year
        goto: function (month, year, callback) {

            this.month = month;
            this.year = year;
            this._generateTemplate(callback);

        },
        gotoPreviousMonth: function (callback) {
            this._move('month', 'previous', callback);
        },
        gotoPreviousYear: function (callback) {
            this._move('year', 'previous', callback);
        },
        gotoNextMonth: function (callback) {
            this._move('month', 'next', callback);
        },
        gotoNextYear: function (callback) {
            this._move('year', 'next', callback);
        }

    };

    var logError = function (message) {

        if (window.console) {

            window.console.error(message);

        }

    };

    $.fn.calendario = function (options) {

        var instance = $.data(this, 'calendario');

        if (typeof options === 'string') {

            var args = Array.prototype.slice.call(arguments, 1);

            this.each(function () {

                if (!instance) {

                    logError("cannot call methods on calendario prior to initialization; " +
                        "attempted to call method '" + options + "'");
                    return;

                }

                if (!$.isFunction(instance[options]) || options.charAt(0) === "_") {

                    logError("no such method '" + options + "' for calendario instance");
                    return;

                }

                instance[ options ].apply(instance, args);

            });

        }
        else {

            this.each(function () {

                if (instance) {

                    instance._init();

                }
                else {

                    instance = $.data(this, 'calendario', new $.Calendario(options, this));

                }

            });

        }

        return instance;

    };

})(jQuery, window);
